﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;
using UCL.Forms;

namespace SoIM
{
    public partial class frmMessageLAN : Form
    {
        /// <summary>
        /// 用户名
        /// </summary>
        public string UserName { get; set; }

        /// <summary>
        /// 头像
        /// </summary>
        public Image UserFace { get; set; }

        /// <summary>
        /// 目标ip
        /// </summary>
        public string TargetIP { get; set; }

        /// <summary>
        /// 目标端口
        /// </summary>
        public string TargetPort { get; set; }

        //创建 1个客户端套接字 和1个负责监听服务端请求的线程    
        Socket socketClient = null;
        Thread threadClient = null;
        Dictionary<string, string> lstFiles = new Dictionary<string, string>(); // 发送的文件列表
        public frmMessageLAN()
        {
            InitializeComponent();
            //关闭对文本框的非法线程操作检查  
            RichTextBox.CheckForIllegalCrossThreadCalls = false;
            TextBox.CheckForIllegalCrossThreadCalls = false;
            this.MoveByControl_(panTitle);
        }

        private void frmMessageLAN_Load(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(TargetPort)) TargetPort = "6066";
            // 配置按钮事件
            btnEventConfig();

            this.Text = string.Format("与{0}交谈中", UserName);
            lblTitle.Text = this.Text;
            picFaceTitle.Image = UserFace;

            try
            {
                ConnectionToServer();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                this.Close();
            }
        }
        private void btnEventConfig()
        {
            btnClose.Click += new EventHandler(btn_Click);
            btnClose2.Click += new EventHandler(btn_Click);
            btnSend.Click += new EventHandler(btn_Click);
            btnSendFile.Click += new EventHandler(btn_Click);
            btnScreenShot.Click += new EventHandler(btn_Click);
            btnSendImage.Click += new EventHandler(btn_Click);
        }
        private void btn_Click(object sender, EventArgs e)
        {
            switch (((Button)sender).Name)
            {
                case "btnClose":
                case "btnClose2":
                    this.Close();
                    break;
                case "btnSend":
                    //调用ClientSendMsg方法 将文本框中输入的信息发送给服务端
                    ClientSendMsg(tbContent.Text.Trim());
                    break;
                case "btnSendFile":
                    //发送文件
                    this.ShowOpenFileDialog("", fn =>
                    {
                        if (fn.Length == 0) return;
                        string filename = Path.GetFileName(fn);
                        lstFiles[filename] = fn;
                        ClientSendMsg("soimc-file-send:" + filename);
                    });
                    
                    break;
            }
        }

        private void ConnectionToServer()
        {
            //定义一个套字节监听  包含3个参数(IP4寻址协议,流式连接,TCP协议)  
            socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //需要获取文本框中的IP地址  
            IPAddress ipaddress = IPAddress.Parse(TargetIP.Trim());
            //将获取的ip地址和端口号绑定到网络节点endpoint上  
            IPEndPoint endpoint = new IPEndPoint(ipaddress, int.Parse(TargetPort.Trim()));
            //这里客户端套接字连接到网络节点(服务端)用的方法是Connect 而不是Bind  
            socketClient.Connect(endpoint);
            //创建一个线程 用于监听服务端发来的消息  
            threadClient = new Thread(RecMsg);
            //将窗体线程设置为与后台同步  
            threadClient.IsBackground = true;
            //启动线程  
            threadClient.Start();
        }

        /// <summary>  
        /// 接收服务端发来信息的方法  
        /// </summary>  
        private void RecMsg()
        {
            while (true) //持续监听服务端发来的消息
            {
                string strRecMsg = null;
                int length = 0;
                //定义一个10M的内存缓冲区 用于临时性存储接收到的信息
                byte[] arrRecMsg = new byte[10 * 1024 * 1024];
                try
                {
                    //将客户端套接字接收到的字节数组存入内存缓冲区, 并获取其长度
                    length = socketClient.Receive(arrRecMsg);
                }
                catch (SocketException ex)
                {
                    tbMsgs.AppendText(string.Format("{0} 套接字异常消息:{1}{2}", DateTime.Now, ex.Message, Environment.NewLine));
                    tbMsgs.AppendText(string.Format("{0} 服务端已断开连接{1}", DateTime.Now, Environment.NewLine));
                    break;
                }
                catch (Exception ex)
                {
                    tbMsgs.AppendText(string.Format("{0} 系统异常消息: {1}{2}", DateTime.Now, ex.Message, Environment.NewLine));
                    break;
                }
                //将套接字获取到的字节数组转换为人可以看懂的字符串
                strRecMsg = Encoding.UTF8.GetString(arrRecMsg, 0, length);
                //将文本框输入的信息附加到txtMsg中  并显示 谁,什么时间,换行,发送了什么信息 再换行
                if (strRecMsg.StartsWith("soimc-file-save:"))
                {
                    strRecMsg = strRecMsg.Substring(11);
                    SendFile(lstFiles[strRecMsg]);
                    tbMsgs.AppendText(string.Format("{0} [服务端]接收了文件：{1}{2}", DateTime.Now, strRecMsg, Environment.NewLine));
                }
                else
                {
                    tbMsgs.AppendText(string.Format("[服务端] {0}{2}{1}{2}", DateTime.Now, strRecMsg, Environment.NewLine));
                }
            }
        }

        /// <summary>  
        /// 发送字符串信息到服务端的方法  
        /// </summary>  
        /// <param name="sendMsg">发送的字符串信息</param>  
        private void ClientSendMsg(string sendMsg)
        {
            //将输入的字符串信息转换为机器可以识别的字节数组
            byte[] arrClientMsg = Encoding.UTF8.GetBytes(sendMsg);
            //实际发送的字节数组比实际输入的长度多1 用于存取标识符
            byte[] arrClientSendMsg = new byte[arrClientMsg.Length + 1];
            arrClientSendMsg[0] = 0;  //在索引为0的位置上添加一个标识符
            Buffer.BlockCopy(arrClientMsg, 0, arrClientSendMsg, 1, arrClientMsg.Length);
            //调用客户端套接字发送字节数组
            socketClient.Send(arrClientSendMsg);
            tbMsgs.AppendText(string.Format("{0} 您向[服务端]发送了:{2}{1}{2}", DateTime.Now, sendMsg, Environment.NewLine));
        }

        /// <summary>
        /// 发送文件的方法
        /// </summary>
        /// <param name="fileFullPath">文件全路径(包含文件名称)</param>
        private void SendFile(string fileFullPath)
        {
            if (fileFullPath == null)
            {
                tbMsgs.AppendText("请选择需要发送的文件!");
                return;
            }
            else if (fileFullPath != null)
            {
                //创建文件流 
                FileStream fs = new FileStream(fileFullPath, FileMode.Open);
                //创建一个内存缓冲区 用于临时存储读取到的文件字节数组
                byte[] arrClientFile = new byte[10 * 1024 * 1024];
                //从文件流中读取文件的字节数组 并将其存入到缓冲区arrClientFile中 
                int realLength = fs.Read(arrClientFile, 0, arrClientFile.Length);  //realLength 为文件的真实长度
                byte[] arrClientSendedFile = new byte[realLength + 1];
                //给新增标识符(实际要发送的)字节数组的索引为0的位置上增加一个标识符1
                arrClientSendedFile[0] = 1;  //告诉机器该发送的字节数组为文件
                //将真实的文件字节数组完全拷贝到需要发送的文件字节数组中,从索引为1的位置开始存放,存放的字节长度为realLength.
                //实际发送的文件字节数组 arrSendedFile包含了2部分 索引为0位置上的标识符1 以及 后面的真实文件字节数组
                Buffer.BlockCopy(arrClientFile, 0, arrClientSendedFile, 1, realLength);
                //调用发送信息的方法 将文件名发送出去
                string filename = Path.GetFileName(fileFullPath);
                ClientSendMsg(filename);
                socketClient.Send(arrClientSendedFile);
                tbMsgs.AppendText(string.Format("{0} 您向服务端发送了文件:{2}{1}{2}", DateTime.Now, filename, Environment.NewLine));
            }
        }

        private void lbFiles_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lbFiles.SelectedIndex == -1) return;
            ClientSendMsg("soimc-file-save:" + lbFiles.SelectedItem.ToString());
        }
    }
}
